﻿using KzwgRoomTester;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

class PacketReceiver : IDestroyable
{
    public long receivedBytes { get; protected set; }
    public bool destroyed { get; protected set; }
    public bool pendingDestroy { get { return false; } }

    private IConnection m_conn;
    private List<PacketObject> m_back;
    private List<PacketObject> m_front;
    private object m_guard_write;
    private Packet m_packet;
    private object m_guard_packet;
    //private Thread m_thread;

    //接受消息长度
    private int msgLen = 4;
    private byte[] msgLenBuff;
    private uint msgTotalSize;


    public PacketReceiver(IConnection conn)
    {
        m_conn = conn;
        m_back = new List<PacketObject>();
        m_front = new List<PacketObject>();

        m_guard_write = new object();
        m_guard_packet = new object();
        m_packet = Packet.Build(0);

        msgLen = 4;
        msgLenBuff = new byte[msgLen];
        msgTotalSize = 0;

        //m_thread = new Thread(StartReceive);

        //m_thread.Start();
        Task.Run(() => StartReceive());
    }

    private void StartReceive()
    {
        while (true)
        {
            if (m_conn == null) break;
            Receive();
        }
    }

    private void Receive()
    {
        try
        {
            if (destroyed) return;

            var count = m_conn.RecvInBuffer();
            if (count > 0)
            {
                OnReceive(count);
            }

        }
        catch (SocketException e)
        {
            Close();
            Logger.LogException(e);
        }
        catch (ThreadAbortException)
        {
            Close();

            Logger.LogInfo("PacketReceiver::StartReceive: Thread aborted!");
            Thread.ResetAbort();
        }
        catch (Exception e)
        {
            Close();
            Logger.LogException(e);
        }
    }

    private void OnReceive(int count)
    {
        receivedBytes += count;
        ProcessData();
    }


    private void ProcessData()
    {
        var received = m_conn.GetRecvLen();
        while (received >= Packet.headerSize)
        {
            if (destroyed || !m_conn.IsConnected) return; // if the session is disconnected or receiver destroyed, stop processing data immdiately


            if (msgTotalSize == 0)
            {
                m_conn.RecvData(msgLenBuff, 0, msgLen);
                msgTotalSize = ByteConverter.ToUInt32(msgLenBuff, 0);
            }

            if (msgTotalSize < Packet.headerSize - msgLen) // packet head  contain message length
            {
                Logger.LogException("PacketReceiver got invalid packet data header, header length is {0} but required {1}", msgTotalSize, Packet.headerSize);
                m_conn.Close();
                return;
            }

            if (received - msgLen < msgTotalSize)
            {
                return;
            }
            var bytes = new byte[msgTotalSize];
            m_conn.RecvData(bytes, 0, (int)msgTotalSize);
            var id = ByteConverter.ToUInt16(bytes, 0);
            var header = id | (long)msgTotalSize << 16;

            msgTotalSize = 0;
            PacketObject o;
            lock (m_guard_packet)
            {
                m_packet.SetWithPos(header, bytes, 2);
                o = PacketObject.Create(m_packet);
                m_packet.Reset();
            }

            if (o != null)
            {
                lock (m_guard_write)
                {
                    m_front.Add(o);
                }
            }
            received = m_conn.GetRecvLen();
        }
    }

    public List<PacketObject> ReceivedPackets()
    {
        lock (m_guard_write)
        {
            var curr = m_front;
            m_front = m_back;
            m_back = curr;

            return m_back;
        }
    }

    public void Destroy()
    {
        if (destroyed) return;
        destroyed = true;

        //m_thread.Abort();
        //m_thread = null;
        m_conn = null;

        lock (m_guard_packet)
        {

            m_packet.Destroy();
            m_packet = null;
        }

        lock (m_guard_write)
        {
            foreach (var p in m_front) p.Destroy();
            foreach (var p in m_back) p.Destroy();

            m_front.Clear();
            m_back.Clear();

            m_front = null;
            m_back = null;
        }

        m_guard_write = null;
        m_guard_packet = null;
    }

    private void Close()
    {
        if (m_conn == null) return;
        m_conn.Close();
    }
}